{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-02T10:50:31.982740Z",
     "start_time": "2020-01-02T10:50:31.976911Z"
    }
   },
   "source": [
    "<style>\n",
    "pre {\n",
    " white-space: pre-wrap !important;\n",
    "}\n",
    ".table-striped > tbody > tr:nth-of-type(odd) {\n",
    "    background-color: #f9f9f9;\n",
    "}\n",
    ".table-striped > tbody > tr:nth-of-type(even) {\n",
    "    background-color: white;\n",
    "}\n",
    ".table-striped td, .table-striped th, .table-striped tr {\n",
    "    border: 1px solid black;\n",
    "    border-collapse: collapse;\n",
    "    margin: 1em 2em;\n",
    "}\n",
    ".rendered_html td, .rendered_html th {\n",
    "    text-align: left;\n",
    "    vertical-align: middle;\n",
    "    padding: 4px;\n",
    "}\n",
    "</style>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Machine Learning with vaex.ml\n",
    "\n",
    "If you want to try out this notebook with a live Python kernel, use mybinder:\n",
    "\n",
    "<a class=\"reference external image-reference\" href=\"https://mybinder.org/v2/gh/vaexio/vaex/latest?filepath=docs%2Fsource%2Ftutorial_ml.ipynb\"><img alt=\"https://mybinder.org/badge_logo.svg\" src=\"https://mybinder.org/badge_logo.svg\" width=\"150px\"></a>\n",
    "\n",
    "\n",
    "The `vaex.ml` package brings some machine learning algorithms to `vaex`. If you installed the individual subpackages (`vaex-core`, `vaex-hdf5`, ...) instead of the `vaex` metapackage, you may need to install it by running `pip install vaex-ml`, or `conda install -c conda-forge vaex-ml`.\n",
    "\n",
    "The API of `vaex.ml` stays close to that of [scikit-learn](https://scikit-learn.org/stable/), while providing better performance and the ability to efficiently perform operations on data that is larger than the available RAM. This page is an overview and a brief introduction to the capabilities offered by `vaex.ml`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:33:40.950813Z",
     "start_time": "2020-01-14T15:33:39.156254Z"
    }
   },
   "outputs": [],
   "source": [
    "import vaex\n",
    "import vaex.ml\n",
    "\n",
    "import numpy as np\n",
    "import pylab as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We will use the well known [Iris flower](https://en.wikipedia.org/wiki/Iris_flower_data_set) and Titanic passenger list datasets, two classical datasets for machine learning demonstrations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:33:40.989833Z",
     "start_time": "2020-01-14T15:33:40.952361Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>145</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>146</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>147</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>148</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>149</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_\n",
       "0    5.9             3.0            4.2             1.5            1\n",
       "1    6.1             3.0            4.6             1.4            1\n",
       "2    6.6             2.9            4.6             1.3            1\n",
       "3    6.7             3.3            5.7             2.1            2\n",
       "4    5.5             4.2            1.4             0.2            0\n",
       "...  ...             ...            ...             ...            ...\n",
       "145  5.2             3.4            1.4             0.2            0\n",
       "146  5.1             3.8            1.6             0.2            0\n",
       "147  5.8             2.6            4.0             1.2            1\n",
       "148  5.7             3.8            1.7             0.3            0\n",
       "149  6.2             2.9            4.3             1.3            1"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = vaex.ml.datasets.load_iris()\n",
    "df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:33:41.135419Z",
     "start_time": "2020-01-14T15:33:40.991423Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "df.scatter(df.petal_length, df.petal_width, c_expr=df.class_);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preprocessing: Scaling of numerical features\n",
    "\n",
    "`vaex.ml` packs the common numerical scalers:\n",
    "\n",
    "* `vaex.ml.StandardScaler` - Scale features by removing their mean and dividing by their variance;\n",
    "* `vaex.ml.MinMaxScaler` - Scale features to a given range;\n",
    "* `vaex.ml.RobustScaler` - Scale features by removing their median and scaling them according to a given percentile range;\n",
    "* `vaex.ml.MaxAbsScaler` - Scale features by their maximum absolute value.\n",
    " \n",
    "The usage is quite similar to that of `scikit-learn`, in the sense that each transformer implements the `.fit` and `.transform` methods."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:33:41.194561Z",
     "start_time": "2020-01-14T15:33:41.136758Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>scaled_petal_length  </th><th>scaled_petal_width   </th><th>scaled_sepal_length  </th><th>scaled_sepal_width  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>0.25096730693923325  </td><td>0.39617188299171285  </td><td>0.06866179325140277  </td><td>-0.12495760117130607</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>0.4784301228962429   </td><td>0.26469891297233916  </td><td>0.3109975341387059   </td><td>-0.12495760117130607</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>0.4784301228962429   </td><td>0.13322594295296575  </td><td>0.9168368863569659   </td><td>-0.3563605663033572 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>1.1039528667780207   </td><td>1.1850097031079545   </td><td>1.0380047568006185   </td><td>0.5692512942248463  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>-1.341272404759837   </td><td>-1.3129767272601438  </td><td>-0.4160096885232057  </td><td>2.6518779804133055  </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...                  </td><td>...                  </td><td>...                  </td><td>...                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>145</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>-1.341272404759837   </td><td>-1.3129767272601438  </td><td>-0.7795132998541615  </td><td>0.8006542593568975  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>146</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>-1.2275409967813318  </td><td>-1.3129767272601438  </td><td>-0.9006811702978141  </td><td>1.726266119885101   </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>147</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>0.13723589896072813  </td><td>0.0017529729335920385</td><td>-0.052506077192249874</td><td>-1.0505694616995096 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>148</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>-1.1706752927920796  </td><td>-1.18150375724077    </td><td>-0.17367394763590144 </td><td>1.726266119885101   </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>149</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>0.30783301092848553  </td><td>0.13322594295296575  </td><td>0.4321654045823586   </td><td>-0.3563605663033572 </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    scaled_petal_length    scaled_petal_width     scaled_sepal_length    scaled_sepal_width\n",
       "0    5.9             3.0            4.2             1.5            1         0.25096730693923325    0.39617188299171285    0.06866179325140277    -0.12495760117130607\n",
       "1    6.1             3.0            4.6             1.4            1         0.4784301228962429     0.26469891297233916    0.3109975341387059     -0.12495760117130607\n",
       "2    6.6             2.9            4.6             1.3            1         0.4784301228962429     0.13322594295296575    0.9168368863569659     -0.3563605663033572\n",
       "3    6.7             3.3            5.7             2.1            2         1.1039528667780207     1.1850097031079545     1.0380047568006185     0.5692512942248463\n",
       "4    5.5             4.2            1.4             0.2            0         -1.341272404759837     -1.3129767272601438    -0.4160096885232057    2.6518779804133055\n",
       "...  ...             ...            ...             ...            ...       ...                    ...                    ...                    ...\n",
       "145  5.2             3.4            1.4             0.2            0         -1.341272404759837     -1.3129767272601438    -0.7795132998541615    0.8006542593568975\n",
       "146  5.1             3.8            1.6             0.2            0         -1.2275409967813318    -1.3129767272601438    -0.9006811702978141    1.726266119885101\n",
       "147  5.8             2.6            4.0             1.2            1         0.13723589896072813    0.0017529729335920385  -0.052506077192249874  -1.0505694616995096\n",
       "148  5.7             3.8            1.7             0.3            0         -1.1706752927920796    -1.18150375724077      -0.17367394763590144   1.726266119885101\n",
       "149  6.2             2.9            4.3             1.3            1         0.30783301092848553    0.13322594295296575    0.4321654045823586     -0.3563605663033572"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "scaler = vaex.ml.StandardScaler(features=features, prefix='scaled_')\n",
    "scaler.fit(df)\n",
    "df_trans = scaler.transform(df)\n",
    "df_trans"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The output of the `.transform` method of any `vaex.ml` transformer is a _shallow copy_ of a DataFrame that contains the resulting features of the transformations in addition to the original columns. A shallow copy means that this new DataFrame just references the original one, and no extra memory is used. In addition, the resulting features, in this case the scaled numerical features are _virtual columns,_ which do not take any memory but are computed on the fly when needed. This approach is ideal for working with very large datasets."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Preprocessing: Encoding of categorical features\n",
    "\n",
    "`vaex.ml` contains several categorical encoders:\n",
    "\n",
    "* `vaex.ml.LabelEncoder` - Encoding features with as many integers as categories, startinfg from 0;\n",
    "* `vaex.ml.OneHotEncoder` - Encoding features according to the one-hot scheme;\n",
    "* `vaex.ml.FrequencyEncoder` - Encode features by the frequency of their respective categories;\n",
    "* `vaex.ml.BayesianTargetEncoder` - Encode categories with the mean of their target value;\n",
    "* `vaex.ml.WeightOfEvidenceEncoder` - Encode categories their weight of evidence value.\n",
    " \n",
    " The following is a quick example using the Titanic dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:33:41.240124Z",
     "start_time": "2020-01-14T15:33:41.195688Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                            </th><th style=\"text-align: right;\">  pclass</th><th>survived  </th><th>name                                           </th><th>sex   </th><th style=\"text-align: right;\">    age</th><th style=\"text-align: right;\">  sibsp</th><th style=\"text-align: right;\">  parch</th><th style=\"text-align: right;\">  ticket</th><th style=\"text-align: right;\">   fare</th><th>cabin  </th><th>embarked  </th><th>boat  </th><th style=\"text-align: right;\">  body</th><th>home_dest                      </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i></td><td style=\"text-align: right;\">       1</td><td>True      </td><td>Allen, Miss. Elisabeth Walton                  </td><td>female</td><td style=\"text-align: right;\">29     </td><td style=\"text-align: right;\">      0</td><td style=\"text-align: right;\">      0</td><td style=\"text-align: right;\">   24160</td><td style=\"text-align: right;\">211.338</td><td>B5     </td><td>S         </td><td>2     </td><td style=\"text-align: right;\">   nan</td><td>St Louis, MO                   </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i></td><td style=\"text-align: right;\">       1</td><td>True      </td><td>Allison, Master. Hudson Trevor                 </td><td>male  </td><td style=\"text-align: right;\"> 0.9167</td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>11    </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Miss. Helen Loraine                   </td><td>female</td><td style=\"text-align: right;\"> 2     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Mr. Hudson Joshua Creighton           </td><td>male  </td><td style=\"text-align: right;\">30     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   135</td><td>Montreal, PQ / Chesterville, ON</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Mrs. Hudson J C (Bessie Waldo Daniels)</td><td>female</td><td style=\"text-align: right;\">25     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "  #    pclass  survived    name                                             sex         age    sibsp    parch    ticket     fare  cabin    embarked    boat      body  home_dest\n",
       "  0         1  True        Allen, Miss. Elisabeth Walton                    female  29             0        0     24160  211.338  B5       S           2          nan  St Louis, MO\n",
       "  1         1  True        Allison, Master. Hudson Trevor                   male     0.9167        1        2    113781  151.55   C22 C26  S           11         nan  Montreal, PQ / Chesterville, ON\n",
       "  2         1  False       Allison, Miss. Helen Loraine                     female   2             1        2    113781  151.55   C22 C26  S           None       nan  Montreal, PQ / Chesterville, ON\n",
       "  3         1  False       Allison, Mr. Hudson Joshua Creighton             male    30             1        2    113781  151.55   C22 C26  S           None       135  Montreal, PQ / Chesterville, ON\n",
       "  4         1  False       Allison, Mrs. Hudson J C (Bessie Waldo Daniels)  female  25             1        2    113781  151.55   C22 C26  S           None       nan  Montreal, PQ / Chesterville, ON"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df =  vaex.ml.datasets.load_titanic()\n",
    "df.head(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:33:41.715848Z",
     "start_time": "2020-01-14T15:33:41.241528Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                            </th><th style=\"text-align: right;\">  pclass</th><th>survived  </th><th>name                                           </th><th>sex   </th><th style=\"text-align: right;\">    age</th><th style=\"text-align: right;\">  sibsp</th><th style=\"text-align: right;\">  parch</th><th style=\"text-align: right;\">  ticket</th><th style=\"text-align: right;\">   fare</th><th>cabin  </th><th>embarked  </th><th>boat  </th><th style=\"text-align: right;\">  body</th><th>home_dest                      </th><th style=\"text-align: right;\">  label_encoded_embarked</th><th style=\"text-align: right;\">  pclass_1</th><th style=\"text-align: right;\">  pclass_2</th><th style=\"text-align: right;\">  pclass_3</th><th style=\"text-align: right;\">  frequency_encoded_home_dest</th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i></td><td style=\"text-align: right;\">       1</td><td>True      </td><td>Allen, Miss. Elisabeth Walton                  </td><td>female</td><td style=\"text-align: right;\">29     </td><td style=\"text-align: right;\">      0</td><td style=\"text-align: right;\">      0</td><td style=\"text-align: right;\">   24160</td><td style=\"text-align: right;\">211.338</td><td>B5     </td><td>S         </td><td>2     </td><td style=\"text-align: right;\">   nan</td><td>St Louis, MO                   </td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">         1</td><td style=\"text-align: right;\">         0</td><td style=\"text-align: right;\">         0</td><td style=\"text-align: right;\">                   0.00305577</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i></td><td style=\"text-align: right;\">       1</td><td>True      </td><td>Allison, Master. Hudson Trevor                 </td><td>male  </td><td style=\"text-align: right;\"> 0.9167</td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>11    </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">         1</td><td style=\"text-align: right;\">         0</td><td style=\"text-align: right;\">         0</td><td style=\"text-align: right;\">                   0.00305577</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Miss. Helen Loraine                   </td><td>female</td><td style=\"text-align: right;\"> 2     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">         1</td><td style=\"text-align: right;\">         0</td><td style=\"text-align: right;\">         0</td><td style=\"text-align: right;\">                   0.00305577</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Mr. Hudson Joshua Creighton           </td><td>male  </td><td style=\"text-align: right;\">30     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   135</td><td>Montreal, PQ / Chesterville, ON</td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">         1</td><td style=\"text-align: right;\">         0</td><td style=\"text-align: right;\">         0</td><td style=\"text-align: right;\">                   0.00305577</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Mrs. Hudson J C (Bessie Waldo Daniels)</td><td>female</td><td style=\"text-align: right;\">25     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">         1</td><td style=\"text-align: right;\">         0</td><td style=\"text-align: right;\">         0</td><td style=\"text-align: right;\">                   0.00305577</td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "  #    pclass  survived    name                                             sex         age    sibsp    parch    ticket     fare  cabin    embarked    boat      body  home_dest                          label_encoded_embarked    pclass_1    pclass_2    pclass_3    frequency_encoded_home_dest\n",
       "  0         1  True        Allen, Miss. Elisabeth Walton                    female  29             0        0     24160  211.338  B5       S           2          nan  St Louis, MO                                            1           1           0           0                     0.00305577\n",
       "  1         1  True        Allison, Master. Hudson Trevor                   male     0.9167        1        2    113781  151.55   C22 C26  S           11         nan  Montreal, PQ / Chesterville, ON                         1           1           0           0                     0.00305577\n",
       "  2         1  False       Allison, Miss. Helen Loraine                     female   2             1        2    113781  151.55   C22 C26  S           None       nan  Montreal, PQ / Chesterville, ON                         1           1           0           0                     0.00305577\n",
       "  3         1  False       Allison, Mr. Hudson Joshua Creighton             male    30             1        2    113781  151.55   C22 C26  S           None       135  Montreal, PQ / Chesterville, ON                         1           1           0           0                     0.00305577\n",
       "  4         1  False       Allison, Mrs. Hudson J C (Bessie Waldo Daniels)  female  25             1        2    113781  151.55   C22 C26  S           None       nan  Montreal, PQ / Chesterville, ON                         1           1           0           0                     0.00305577"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "label_encoder = vaex.ml.LabelEncoder(features=['embarked'])\n",
    "one_hot_encoder = vaex.ml.OneHotEncoder(features=['pclass'])\n",
    "freq_encoder = vaex.ml.FrequencyEncoder(features=['home_dest'])\n",
    "\n",
    "df = label_encoder.fit_transform(df)\n",
    "df = one_hot_encoder.fit_transform(df)\n",
    "df = freq_encoder.fit_transform(df)\n",
    "\n",
    "df.head(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-02T13:09:43.742926Z",
     "start_time": "2020-01-02T13:09:43.676031Z"
    }
   },
   "source": [
    "Notice that the transformed features are all included in the resulting DataFrame and are appropriately named. This is excellent for the construction of various diagnostic plots, and engineering of more complex features. The fact that the resulting (encoded) features take no memory, allows one to try out or combine a variety of preprocessing steps without spending any extra memory. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dimensionality reduction \n",
    "\n",
    "### Principal Component Analysis\n",
    "\n",
    "The [PCA](https://en.wikipedia.org/wiki/Principal_component_analysis) implemented in `vaex.ml` can scale to a very large number of samples, even if that data we want to transform does not fit into RAM. To demonstrate this, let us do a PCA transformation on the Iris dataset. For this example, we have replicated this dataset thousands of times, such that it contains over **1 billion** samples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:33:41.735059Z",
     "start_time": "2020-01-14T15:33:41.717248Z"
    },
    "tags": [
     "skip-ci"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of samples in DataFrame: 1,005,000,000\n"
     ]
    }
   ],
   "source": [
    "df = vaex.ml.datasets.load_iris_1e9()\n",
    "n_samples = len(df)\n",
    "print(f'Number of samples in DataFrame: {n_samples:,}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:34:28.406884Z",
     "start_time": "2020-01-14T15:33:41.737141Z"
    },
    "tags": [
     "skip-ci"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[########################################] 100.00% elapsed time  :    25.39s =  0.4m =  0.0h\n",
      "[########################################] 100.00% elapsed time  :    21.16s =  0.4m =  0.0h      \n",
      " "
     ]
    }
   ],
   "source": [
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "pca = vaex.ml.PCA(features=features, n_components=4, progress=True)\n",
    "pca.fit(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The PCA transformer implemented in `vaex.ml` can be fit in well under a minute, even when the data comprises 4 columns and 1 billion rows. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:34:28.490848Z",
     "start_time": "2020-01-14T15:34:28.411677Z"
    },
    "tags": [
     "skip-ci"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                                        </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>PCA_0              </th><th>PCA_1                </th><th>PCA_2                </th><th>PCA_3                </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>            </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>-0.5110980606779778</td><td>0.10228410712350186  </td><td>0.1323278893222748   </td><td>-0.05010053509219568 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>            </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>-0.8901604458458314</td><td>0.03381244392899576  </td><td>-0.009768027251340669</td><td>0.15344820595853972  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>            </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>-1.0432977815146882</td><td>-0.22895691422385436 </td><td>-0.4148145621997159  </td><td>0.03752355212469092  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>            </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>-2.275853649499827 </td><td>-0.33338651939283853 </td><td>0.28467815929803336  </td><td>0.062230280587310186 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>            </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>2.5971594761444177 </td><td>-1.1000219272349778  </td><td>0.1635819259153647   </td><td>0.09895807663018358  </td></tr>\n",
       "<tr><td>...                                      </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...                </td><td>...                  </td><td>...                  </td><td>...                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,995</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>2.639821267772449  </td><td>-0.31929007064114523 </td><td>-0.13925337154239886 </td><td>-0.06514104661032082 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,996</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>2.537573370562511  </td><td>-0.5103675440827672  </td><td>0.17191840827679977  </td><td>0.19216594922046545  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,997</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>-0.2288790500828927</td><td>0.402257616677128    </td><td>-0.22736271123587368 </td><td>-0.018620454169007566</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,998</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>2.199077960400875  </td><td>-0.8792440918495404  </td><td>-0.1145214537809282  </td><td>-0.025326936252885096</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,999</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>-0.6416902785957136</td><td>-0.019071179119340448</td><td>-0.20417287643043353 </td><td>0.02050967499165212  </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#              sepal_length    sepal_width    petal_length    petal_width    class_    PCA_0                PCA_1                  PCA_2                  PCA_3\n",
       "0              5.9             3.0            4.2             1.5            1         -0.5110980606779778  0.10228410712350186    0.1323278893222748     -0.05010053509219568\n",
       "1              6.1             3.0            4.6             1.4            1         -0.8901604458458314  0.03381244392899576    -0.009768027251340669  0.15344820595853972\n",
       "2              6.6             2.9            4.6             1.3            1         -1.0432977815146882  -0.22895691422385436   -0.4148145621997159    0.03752355212469092\n",
       "3              6.7             3.3            5.7             2.1            2         -2.275853649499827   -0.33338651939283853   0.28467815929803336    0.062230280587310186\n",
       "4              5.5             4.2            1.4             0.2            0         2.5971594761444177   -1.1000219272349778    0.1635819259153647     0.09895807663018358\n",
       "...            ...             ...            ...             ...            ...       ...                  ...                    ...                    ...\n",
       "1,004,999,995  5.2             3.4            1.4             0.2            0         2.639821267772449    -0.31929007064114523   -0.13925337154239886   -0.06514104661032082\n",
       "1,004,999,996  5.1             3.8            1.6             0.2            0         2.537573370562511    -0.5103675440827672    0.17191840827679977    0.19216594922046545\n",
       "1,004,999,997  5.8             2.6            4.0             1.2            1         -0.2288790500828927  0.402257616677128      -0.22736271123587368   -0.018620454169007566\n",
       "1,004,999,998  5.7             3.8            1.7             0.3            0         2.199077960400875    -0.8792440918495404    -0.1145214537809282    -0.025326936252885096\n",
       "1,004,999,999  6.2             2.9            4.3             1.3            1         -0.6416902785957136  -0.019071179119340448  -0.20417287643043353   0.02050967499165212"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_trans = pca.transform(df)\n",
    "df_trans"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Recall that the transformed DataFrame, which includes the PCA components, takes no extra memory. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Clustering\n",
    "\n",
    "### K-Means\n",
    "\n",
    "`vaex.ml` implements a fast and scalable K-Means clustering algorithm. The usage is similar to that of `scikit-learn`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:34:29.460510Z",
     "start_time": "2020-01-14T15:34:28.492880Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Iteration    0, inertia  519.0500000000001\n",
      "Iteration    1, inertia  156.70447116074328\n",
      "Iteration    2, inertia  88.70688235734133\n",
      "Iteration    3, inertia  80.23054939305554\n",
      "Iteration    4, inertia  79.28654263977778\n",
      "Iteration    5, inertia  78.94084142614601\n",
      "Iteration    6, inertia  78.94084142614601\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>prediction_kmeans  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>0                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>0                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>0                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>1                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>2                  </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...                </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>145</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>2                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>146</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>2                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>147</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>0                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>148</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>2                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>149</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>0                  </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    prediction_kmeans\n",
       "0    5.9             3.0            4.2             1.5            1         0\n",
       "1    6.1             3.0            4.6             1.4            1         0\n",
       "2    6.6             2.9            4.6             1.3            1         0\n",
       "3    6.7             3.3            5.7             2.1            2         1\n",
       "4    5.5             4.2            1.4             0.2            0         2\n",
       "...  ...             ...            ...             ...            ...       ...\n",
       "145  5.2             3.4            1.4             0.2            0         2\n",
       "146  5.1             3.8            1.6             0.2            0         2\n",
       "147  5.8             2.6            4.0             1.2            1         0\n",
       "148  5.7             3.8            1.7             0.3            0         2\n",
       "149  6.2             2.9            4.3             1.3            1         0"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import vaex.ml.cluster\n",
    "\n",
    "df = vaex.ml.datasets.load_iris()\n",
    "\n",
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "kmeans = vaex.ml.cluster.KMeans(features=features, n_clusters=3, max_iter=100, verbose=True, random_state=42)\n",
    "kmeans.fit(df)\n",
    "\n",
    "df_trans = kmeans.transform(df)\n",
    "df_trans"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "K-Means is an unsupervised algorithm, meaning that the predicted cluster labels in the transformed dataset do not necessarily correspond to the class label. We can map the predicted cluster identifiers to match the class labels, making it easier to construct diagnostic plots."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:34:29.490949Z",
     "start_time": "2020-01-14T15:34:29.462154Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>prediction_kmeans  </th><th>predicted_kmean_map  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>0                  </td><td>1                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>0                  </td><td>1                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>0                  </td><td>1                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>1                  </td><td>2                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>2                  </td><td>0                    </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...                </td><td>...                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>145</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>2                  </td><td>0                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>146</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>2                  </td><td>0                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>147</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>0                  </td><td>1                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>148</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>2                  </td><td>0                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>149</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>0                  </td><td>1                    </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    prediction_kmeans    predicted_kmean_map\n",
       "0    5.9             3.0            4.2             1.5            1         0                    1\n",
       "1    6.1             3.0            4.6             1.4            1         0                    1\n",
       "2    6.6             2.9            4.6             1.3            1         0                    1\n",
       "3    6.7             3.3            5.7             2.1            2         1                    2\n",
       "4    5.5             4.2            1.4             0.2            0         2                    0\n",
       "...  ...             ...            ...             ...            ...       ...                  ...\n",
       "145  5.2             3.4            1.4             0.2            0         2                    0\n",
       "146  5.1             3.8            1.6             0.2            0         2                    0\n",
       "147  5.8             2.6            4.0             1.2            1         0                    1\n",
       "148  5.7             3.8            1.7             0.3            0         2                    0\n",
       "149  6.2             2.9            4.3             1.3            1         0                    1"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_trans['predicted_kmean_map'] = df_trans.prediction_kmeans.map(mapper={0: 1, 1: 2, 2: 0})\n",
    "df_trans"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can construct simple scatter plots, and see that in the case of the Iris dataset, K-Means does a pretty good job splitting the data into 3 classes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:34:29.827109Z",
     "start_time": "2020-01-14T15:34:29.492234Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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NNAC2ArcBrgBrLWPVSx9+zDly+oWApdYa/ebRgotEZHGrZ5XEVRWiYhIndWUVXV5DladWWvP3c/rFrgqkTWIiIjsi7JKRETiKdn3YImIiIiIiDQaarBERERERETiRA2WiIiIiIhInKjBEhERERERiRM1WCIiIiIiInGiBktERERERCRO1GCJiIiIiIjEiRosERERERGROFGDJSIiIiIiEidqsEREREREROJEDZaIiIiIiEicqMESERERERGJEzVYIiIiIiIicaIGS0REREREJE7UYImIiIiIiMSJGiwREREREZE4UYMlIiIiIiISJ2qwRERERERE4kQNloiIiIiISJyowRIREREREYkTNVgiIiIiIiJxogZLREREREQkTtRgiYiIiIiIxIkaLBERERERkThRgyUiIiIiIhInarBERERERETiRA2WiIiIiIhInKjBEhERERERiRM1WCIiIiIiInGiBktERERERCRO1GCJiIiIiIjEiRosERERERGROFGDJSIiIiIiEidqsEREREREROLElewCmrKysjLmfDifxdOXk902i9HnjCSteWqyyxIREam0d0cun7/yNbu27qbf0X0YOLYvDofOz4qI1EQNVpKUFJXw6+P+wNrF6ynKL8Yb8PDUTS9z72e30WNQ12SXJyIiwrdfLeWmk+4iXBYmWBTkPw9+QM/BXbn7w5txe9zJLk9EpEHSKagkeeP+91i1cC1F+cUAlBQGKcwt4s5z/o61NsnViYhIUxcOh/njWX+jOL+YYFEQgOL8YpbN+p73n/osydWJiDRcarCS5NMXvqgMrKp2btrNltXbklCRiIjIj1YvWld5ErCqksIgHz83pf4LEhE5RKjBShKHM/an3lqLcZh6rkZERCSSw+mo8YqKmjJMRETUYCXNiZccizfgiRgzBlp3aUmrTjlJqkpERKRcpz7tSc9Kixr3BrycdNmYJFQkInJoUIOVJKf9cjx9juyFL8WLy+3En+ojLTON3792fbJLExERwRjD7W/+hpRmAfypPlxuJ96Al0HH9+OEi0cnuzwRkQZLqwgmidvj5p6PbmHxtOUsnf4dWW0zGXnaELx+b7JLExERAaD7wC68suFxvnpzJnu27qXv0YfRa2j3ZJclItKgqcFKImMMh4/sxeEjeyW7FBERkZj8KT6Ov/CYZJchInLI0CWCIiIiIiIicaIGS0REREREJE50iWADtGf7Xj58+n+sXLCG7oO6Mu7SY0nPjF7JSUREJFmWzPiOj5+bQrA4yDFnHcnQ8QMwRo8ZERFJeINljBkHPAA4gaestfdUe3008DawumLoTWvtHYmuq6Fat2wj1xx5M8HiIMHiUqa/M4d//fktHp55N627tEx2eSIijY5yqu5e/tPrvHLPWwSLglhrmfrGDIZNGMTNr1yrJktEmryEXiJojHECjwDjgd7AucaY3jE2nWqt7V/xq0mH1gNXPkHB3gKCxaUAlBQFydudzyPXPJPkykREGh/lVN1tW7edf971JiWFJZUPIi4uKGHme3OZ//m3Sa5ORCT5En0P1lBghbV2lbU2CLwKnJrgfR6ywuEwi6YupSKvKtmwZd6nC5NTlIhI46acqqM5Hy3AOKLfpSouKOHrt2YnoSIRkYYl0Q1WW2B9lb9vqBirboQxZoEx5gNjTJ9YExljLjfGzDHGzNm+fXsiak06YwwutzPma26vu56rERFpEuKWU9A0ssqX4sXhjP7xwelyEkjzJaEiEZGGJdENVqwLsau9P8M8oKO19gjgIeCtWBNZa5+w1g621g7Ozs6Oc5kNgzGGY885Crc38tY4t9fNCT8ZnZyiREQat7jlFDSNrBp+8mBsuPqnCJxuJ2P1vCwRkYQ3WBuA9lX+3g7YVHUDa22utTa/4s/vA25jTIsE19Vg/eKBS+jWvzO+FC/+VB/egJfDhnfnp3efn+zSREQaI+VUHQXS/Pzhrd/iT/URSPcTSPPj8bm5+sFL6dAr1pt/IiJNS6JXEZwNdDfGdAY2AucA51XdwBjTCthqrbXGmKGUN307E1xXg5WSHuCBaXfy3ZyVrF++iU592tNtQOdklyUi0lgppw7AwDF9eW3LU8z9eAGlJaUMPL6fHiciIlIhoQ2WtTZkjLka+Ijy5W+fsdYuNsZcUfH6Y8Bk4EpjTAgoAs6xtvoyD02LMYaeQ7rRc0i3ZJciItKoKacOnC/gZeRpQ5NdhohIg2MOxYwYPHiwnTNnTrLLEBGRBDHGzLXWDk52HQdDWSUi0rjVlFWJvgdLRERERESkyUj0PViN1uJpy/nPg++xY+Muhk8YxMQrTiA1IyXmtru27OaWiXezcsFaHE4HR08ezg3PX03+7gLefuRD5n26kJYdc5h03QR6DOrKxhWbeePv77Jy/hp6DO7KpOsm0qpTTsy5rbVMe3s27z35CcGiUsacP4qxFx6N26Nl3UVEmrJgcZCPnv2cKf+aRiDdz8lXnsjQ8QNq3P7jF6bw6HXPUbi3kNTMVK599HJGTRrOkunLefPB99mxYSdDTxrIKVeeSEqzAF+/NYv3n/yUYEkpY84bxfEXHYPLHfvHitydebz9yIfM/WQBOR2ymXTdRHoO7pqoQxcRSSpdIngAPnjmMx751TMEi4JYCx6/m+Y5GTw67y+kNU+N2DZ/bz5nZF0ataRtRk46xjgo2FtAsLgU4zB4fG4uuGUyL9/5JqUlpZSFynC5nbh9bu6f+ie69OsYVctDVz/Fx89PobigBABvwEvPIV35y6e34nTGfqaWiEhDp0sED05psJTrRv2eNYvXU1IYBMqfX3XaL8fz07uiV6V99vev8M8734waP/acI5n2zlyCRSXleefzkJGTzoAxffnitWmV2eNL8dJzSDf+/Mnvo7Jn97a9XDHgN+Tvzq+Sdx5+88wvOOasIxNw9CIi9UOXCMZJsDjIP659jpLC8uYKIFhUyq6te/jPg+9Hbf/Hs/4e83khe7blsmfHXoLFpQDYsKWkMMizv3+V4oJiykJlAIRKyyjKK+Yf1z4bNceG7zfz4TP/qww4gJLCEr6bu4qZ782Lx+GKiMgh6Mt/z2Dtkg2VzRVAcUEJb/z9PXZsjF4A8Z93RTdXAJ+/Oo2SwpIf8644yK4te/jkxS8isqe4oITlc1Yy+4P5UXO8es9/yN2RWy3vSrj/yicqs05EpDFRg1VHKxesxeGIfi5laXEp09+ZHTW++OtlNc5ly6Ibr3BZOOa2i79eHjW2cMpijCP6n7A4v5jZH3xT435FRKRxm/HunIgG6Aduj4tFUyNzqaioJPrRyvsQCoawMbKqOL+YWR9GZ8/M9+YRKo1upMpKy9jw3aaocRGRQ50arDpKy0ylLEZQAGRkN4sa86V447Jff5ovZi0OZ/Q/ocvjJCMnuhYREWkamrdsFjMfoDw7qvJ46n47tolxotHlcdE8Jz1qPD0rNWoMIBQqI7V57NdERA5larDqqF331nQ4rF1UcPkCXs64dkLU9hfdfnaNc3l8kQtRON1Osttl4fFHjnv9Hk75xYlRHz9swkBcruh/QqfTyQkXj97XYYiISCN20s+Oxx2jcfIGPAw47vCIMafTSXpm7EbH5XZG5Z034MXj80Rt63Q6OP6i0VHjk647Oepko8vtpPfwHmS1br6/QxEROeSowToAd7zzWzr1aY834CWQ7i9fnOLWyQwZF7060ylXnsigE/pFjV/xt58w+fqT8fjcpDQL4PV76D6wCw9Mv5Oh4wZWjnt8bo48bSgX/H5y1Bwen4c/f3IrWa2b40/zEUj3E0j387t/Xkvrzi0TcuwiItLwderTnuufugJfank2+FN95HTM5t7PbsPpil4A6clv/4bbW+3kXoqXJ7/9G537dsBXJe/Ov2US9035A5mtMyKy5+ZXr4u54u3Rk4cz6fqJP+ZdwEO3AZ255V/XJez4RUSSSasIHoTV365jz7a99BjUhZRmsZdo/8H2jTv51z1vkd4ijfNvmVS5ylLuzjxWLlhDVptMOvRqW7n9tnXb2fD9Ftr3bEN2u6x9zh0Oh1k+eyWlJaX0GtYdj1dLtIvIoU2rCMZHSVEJy2atwJfio8egLhgTfWlfVVPfmMGcj+cz8tQhDD1pUOX4msXr2b11D90Hdql8JEldsyd3Vx4r568hs3VzOh7W7uAPTkQkyWrKKjVYIiLS4KjBEhGRhk7LtIuIiIiIiCSYGiwREREREZE4qfvarALAUze9zDuPfEiwuJR2PVtzw7NX0b5XW647+lZWLlgDFlp1yuYvn91GemYqr937Dl/8ezq+FC+n/GIc4y49FkeMZ1gBLJ+9ghf/+DprF6+n6xGduODWyXTr37l+D1BERA5pW9du467zHmD5nJU4XQ5Gnz2S6574OVPfmMH9P3+CwtwiHE4HYy4YxQ3PXs2yWd/z0h9fZ+2SDXTt34kLbz2Trkd0ijl3WaiMd5/4hPce/4TSYIjjzj2KyddPxJ/qr9+DFBFpgHQP1gG4afyfmPPRgogxY8qXWQ8Fo5+R1bprS3Zs2EVpSflT7H0pXo4+cwS/eeaqqG3nfbaIW0+9h2BREGvBGIPH7+HPH/+ePkf2TMwBiYg0MLoH6+Ds2ZHLOW0vj3puY/OWzdi9dW/U9u16tmH7+h1R2fOXT35P7xHR2XPHmX9l1gfzKSksf5ixx+emXY82PDzrbtweLbQkIk2D7sGKkx2bdkU1VwDWErO5Ati6ZltlcwVQXFDClFe/ZtPKLVHbPnLNM5QUlgdc+byWksISHr3u2fgcgIiINHqPXfdcVHMFxGyuADYs3xQ7e65/PmrblQvWMOuDbyqbK4BgcSmbV21l2luz43MAIiKHMDVYdTTr/Xl1/phwWfS7hE63k6Uzvo/cLhxm3ZINMedYMX9NnfcrIiJN08IvlsRlnhXzVkeNLZn+HcS4+KUov5j5UxbHZb8iIocyNVh1FM9nd2S1iXyCvTGGQHrs69fTM1Pjtl8REWncmrfOiMs86S3Sosay2jSP+bBij89Ny44t4rJfEZFDmRqsOuozshcpGYE6fYzbF3k9usPpoFlWOv2O6R0xbozhjGsm4A14Isa9AS9n/t8pB1awiIg0OZf/+cLYL9TwnGGH01FD9pwcte3Q8QPwpXijHlrscDo54SejD6RcEZFGRQ3WAXj8m3tJz6pyVs/AhMvHcv2TV0SF11k3nMqf/nsTma0z8KV48fjcdB/Ymfum3B5zFcELbp3M+J+OweNzE0jz4/V7OOUXJzLp+okJPioREWksjhjdh8vuOR/j+DGUfCleHpx2J90GdorY1u1z88zS+znxkmMjsue0X45n0nXR2eNyu/jbl3fQuW8HPH4PvoCX7PZZ3P3hzWS2ah61vYhIU6NVBA/C6m/XsWX1VgaM6Ysv4Kscn/rGDAryijj+wqNxOssvowiHw2z8fjO+FB/Z7bL2O3dBbiHb1++kZccWWvZWRJocrSIYH6FQiHmfLiQ9K51eQ7pVju/ZvpcvXptG90Fd6D38x1UC65o9W9dupzQYom23VlHvaImINHY1ZZUaLBERaXDUYImISEOnZdpFREREREQSTA2WiIiIiIhInLiSXUB927FxJ0/e+DKz3p+H1+9lws/Hcu6Np+Nyx/5UvHD7v/jXX94mWFyKL8XLT/5wNqf9ajwXdrmKHRt2AeWr/11w65n0HNqVWybcHfHxF/3hLIzTwfO3vFo5ZpyGZ5Y+wPO3/ospr35dOd4sO41XNjzON59+y7O3vMKmlVto37Mtl951HgPH9I1ZX0lRCS//6Q0+fPZzQsEQI08fyqV3nkfznGYH+6kSEZEkmfHuXJ77/StsXr2NDoe147K7z+eI0X1ibrtnRy63TLyb7+asxFpL+55t+ePbv2Xp7BXc+5OHCZeFAQhkBHh6yf38btyfWL1wXeXHO5yGt/Ne4KeHXce2tTsqx9sf1pbH59/LWS0vI39PYeX4xCuO54r7fsJLf3ydj579nFBpGSNPH8pP7zqPjOzY2bNq4VqeuOFFls74nuY56Zxz4+mceMmxum9LRBqlJnUPVv6eAi7pdQ25O/MqA8fr9zB4XH9uf+M3Uds/et2zvPnA+1HjDreDcGm47oXXgtPlwOVxUVIYrBzz+j3c9savGTJuQMS21lp+fdztLJv5PcHi0vKPdzvJat2cp5fcjy/gTUiNIiKJ1pTvwfrfq1/xt8sejcqBO965MepkWzgc5rTmP6Eorzhi3OF0VOZcImS3z2Lv9tyI7GnRJpOnl/wdrz8ye9Yt28hVQ2+kOP/HGn0BL2fdcCoX3npmwmoUEUk03YMFfPD0ZxTlFUWETklRkNkfzmfdso1R27/18Icx50lUcwVQFgpHhCqU1/j4b16I2nb57BV8N2dlZcABlJWWkbszL+KdMREROTRYa3nyNy/GzIEnb4jOgXcf+ziquQIS2lwBbF+/Myp79u7IZcq/pkVt+9Id/yZYWBIxVlxYwr/+/BZFBdG1i4gc6ppUg7V42nJKioJR406Xg1UL1kSMFeYXJTyg6mLDd5ujxlZ8s4ZY70AWF5SwdOb39VGWiIjEUWlJKbs274752rql0ScC53++ONEl1VpxQQnLZkVnz/LZKwiHo7PK4XKydc32+ihNRKReNakGq+Nh7XB7o++1smFL6y4tI8Z8AW+NT7xPhlgPb2zdJQeHM/qf0Ov30L5Xm/ooS0RE4sjtdRNID8R8LattZtRYpz7tEl1SrXkDHtr3bBs13qZ765jbh4IhstrowcQi0vg0qQZr4hUnRC1m4XI7ad+zLT0Gd40YdzgcDB0fec9TffFWu3fKG/By4W2To7YbMKYvzVtm4HQ5K8eMAZfHxQk/GZ3oMkVEJM6MMZz929Oi7qH1BrxcdNtZUdufc+PpOF31H+W+FG/Efo0Bt8fN2AuPjtr2/Jsn4Q14Isa8fg/HnjOStOapCa9VRKS+NakGK7tdFvd+dhud+3bA6XLi8rgYNnEQ93x8S8yVjP74zo0MPL7fjwMGjj5zOM999yBOtzNi2wFj+zLxyhOi5uh9ZHcGxFgB8NrHL6d1l5yIMeMwvLD6ES68dTIpzQK4PS5Sm6dw6V3nMu6S46LmcDgc/P3LOxh4fD9cbidOt5Pug7ry96l/JD0zrbafFhERaUDOvuFUzrnpdALpftweF+lZqVx+74WMvSC6efH4PDw4427SM39sVHwpXu5450Ym/nxsxLbGYbjno1vw+KKv5PjH/D9HNWpun4unl95P9XjsckRHnvvuIQaO6YezFtlz+Mhe3PTSNWS3z8LlceHxexh36XFc+/jldfm0iIgcMprUKoJVFewtwOVxRa12FEswWMr29Tto2TEbl+vHYNqxZTcbv9tEnyN7RozP/GAeebsLGHveqIh5pr87l8xWzeg5uFvlWGlpKXM+XkDnvh1p1SG7crysrIyCvYWkNAvgdEY2c7EUF5YQLgsTSPPvd1sRkYauKa8i+IOyUBkFuYWkZqTgcOz/fOieHbmEgiFatIm8lHDZ7BWkNU+hbbcfL9UrLi7mvSc+Y9AJ/ejUq33l+K5decx9by6DJgwis0qztGXddlYvWsvgE47A7Xb/OE8dssdaS/6eAnwpXtwe9363FxFp6GrKqibbYImISMOlBktERBo6LdMuIiIiIiKSYGqwqonHO3rW2pjzhEIhQqHQQc8vIiJNW7yyKhbllIjIwYm+07WJmvfpQh655hnWLd1IavMUJl9/MufedHqtrnv/Qe6uPB751TN8+foMwmVhBo3tx68e/RmfvPQFL9z6WuV2TreTB6ffRY+BXRJxKCIi0giFSkM8e8sr/PexjynOL6HrgE788uHL6D28R53mmffZIv5xzTOsXbKB1IwUJl0/kfN+dwY/6f5LtqzeVrldpz7teHLR3+N9GCIijZ7uwQKWzPiOG8b+gZLCHx9C7A14OeUXJ3D5Xy6q1RzhcJjLj/g1G7/bRKi0DACHw+BL9VGYWxTzYz4J//vgixcRaYR0D1a0P1/0EFPfmEFJ0Y9Z5Qt4eXj2PXQ8rHbPw1o263t+fdztUXnncBqK8oqjtu/avxOPzbv34IsXEWmEdA/WPrxw+2sRYQNQUljCO498RFFBdODEMv9/37Jt7fbK5gogHLY1NlcAD1z5+IEVLCIiTcquLbv58vXpEc0VQLCklH/f+3at53nxD/+OmXexmiuAlfPX1LlWEZGmTg0WsG7phpjjDqeDnZt212qO9cs3URYq2/+GVSz8clmdthcRkaZp08qtuL3RS5uHy8KsqEMTtGbx+jhWJSIisajBAjof3iHmeLgsTIu2mTFfq65j73Y4Xft/XlVVA47rU6ftRUSkaWrbvTXB4tKocYfTQfc63M/bpV/HqAcHi4hIfKnBAi66/Sy8AU/EmDfg5YxrJ+AL7P9BxABHjO5Dm26tcHt+XDfE4XSQmpFS48dc/dBlB1awiIg0Kc1zmnHc+UdFZZXH5+HsG06t9TwX3nYmHn903qVmBGJu32tY97oXKyLSxKnBAnoO6cad7/2ObgM64XQ5aN6yGRffcTaX/OncWs9hjOG+z29nzPmjyp9S73Ux4pTBPLHwPi6/90KocsbQ7XXx5KL7EnAkIiLSWF332M8569enkJaZitPloPeIHtw35Xba9WhT6zl6DOrK3R/cQreBnXG6HGS0bMZFt5/JGzuepf1hbSO27T6oMw9NvyvehyEi0uhpFUEREWlwtIqgiIg0dFpFUEREREREJMHUYImIiIiIiMSJGiwREREREZE4SXiDZYwZZ4xZboxZYYy5McbrxhjzYMXrC40xAxNdk4iIyA+UUyIiEk8JbbCMMU7gEWA80Bs41xjTu9pm44HuFb8uBx5NZE0iIiI/UE6JiEi8JfodrKHACmvtKmttEHgVqP7AjlOBF2y5GUCGMaZ1gusSEREB5ZSIiMRZohustsD6Kn/fUDFW120wxlxujJljjJmzffv2uBcqIiJNUtxyCpRVIiKS+AbLxBir/uCt2myDtfYJa+1ga+3g7OzsuBQnIiJNXtxyCpRVIiKS+AZrA9C+yt/bAZsOYBsREZFEUE6JiEhcJbrBmg10N8Z0NsZ4gHOAd6pt8w5wUcUqTcOBvdbazQmuS0REBJRTIiISZ65ETm6tDRljrgY+ApzAM9baxcaYKypefwx4HzgJWAEUApfsb965c+fuMMasjUOJLYAdcZinodNxNi46zsZFxxlbx0QVUlWicgrillX6/9H4NJVj1XE2LjrO2GJmlbE25mXkTYIxZo61dnCy60g0HWfjouNsXHScsi9N5fPWVI4Tms6x6jgbFx1n3ST8QcMiIiIiIiJNhRosERERERGROGnqDdYTyS6gnug4GxcdZ+Oi45R9aSqft6ZynNB0jlXH2bjoOOugSd+DJSIiIiIiEk9N/R0sERERERGRuFGDJSIiIiIiEidNssEyxjxjjNlmjPk22bUkijGmvTHmc2PMUmPMYmPMNcmuKRGMMT5jzCxjzIKK4/xDsmtKJGOM0xjzjTHm3WTXkkjGmDXGmEXGmPnGmDnJridRjDEZxpjXjTHLKr5WRyS7pngzxvSs+Hf84VeuMebaZNfV0DWFnAJlVWPVFLJKOdV4JCKnmuQ9WMaYo4F84AVr7eHJricRjDGtgdbW2nnGmDRgLnCatXZJkkuLK2OMAVKstfnGGDfwFXCNtXZGkktLCGPM9cBgIN1aOzHZ9SSKMWYNMNha26gfamiMeR6Yaq19yhjjAQLW2j3JritRjDFOYCMwzFobj4fFN1pNIadAWaWsOnQppxqneOVUk3wHy1r7JbAr2XUkkrV2s7V2XsWf84ClQNvkVhV/tlx+xV/dFb8a5VkDY0w7YALwVLJrkYNnjEkHjgaeBrDWBhtzaFUYA6xUc7V/TSGnQFmVxJISRlnVeCinDlyTbLCaGmNMJ2AAMDO5lSRGxaUI84FtwCfW2kZ5nMD9wA1AONmF1AMLfGyMmWuMuTzZxSRIF2A78GzFpTRPGWNSkl1Ugp0DvJLsIqRhUlY1Gk0lq5RTjVNcckoNViNnjEkF3gCutdbmJrueRLDWlllr+wPtgKHGmEZ3OY0xZiKwzVo7N9m11JOR1tqBwHjgqorLpRobFzAQeNRaOwAoAG5MbkmJU3FpySnAv5NdizQ8yqrGoYlllXKqkYlnTqnBasQqrvN+A3jZWvtmsutJtIq3racA45JcSiKMBE6puOb7VeA4Y8xLyS0pcay1myp+3wb8Bxia3IoSYgOwocpZ7NcpD7LGajwwz1q7NdmFSMOirGpUmkxWKacapbjllBqsRqrihtqngaXW2r8lu55EMcZkG2MyKv7sB8YCy5JbVfxZa2+y1raz1nai/O3r/1lrL0hyWQlhjEkLUGoAAAQ+SURBVEmpuNmdiksRTgAa3Upq1totwHpjTM+KoTFAo7qxv5pz0eWBUo2yqnFpKlmlnGq04pZTrnhMcqgxxrwCjAZaGGM2ALdZa59OblVxNxK4EFhUcc03wO+ste8nsaZEaA08X7HqiwN4zVrbaJeFbSJaAv8p/7kLF/BPa+2HyS0pYX4JvFxxWcIq4JIk15MQxpgAcDzw82TXcqhoIjkFyio5NCmnGpl451STXKZdREREREQkEXSJoIiIiIiISJyowRIREREREYkTNVgiIiIiIiJxogZLREREREQkTtRgiYiIiIiIxIkaLBERERERkThRgyWSQMaYi40xbWqx3XPGmMn7eH2KMWZwnGvLMMb8osrfRxtj9FwWEZEmRDklEn9qsEQS62Jgv8GVJBnAL/a7lYiINGYXo5wSiSs1WCJ1YIzpZIxZZox53hiz0BjzujEmYIz5//buJ8SqMozj+PfXXxVbFLloM9gfSkrC0qDSTRADgRsjAmnRqpWbiP4h4p9FULQ0sgyiRRJUZFCLZEAIFIdMmVKxTVAYtMgImoWMJE+L+w5ehmaGExeOM3w/m/Pe55zzvs9dXB7ec95z7sYk3yY5leRIkjvalb5NDP4BfSrJyiS7k5xMcjbJwbS/ge+Yw3iSE0lOJ/ksyeoW/yXJvhY/k2Rdi69JMtHi7yf5NcntwJvA3S23t1v3q9t3+inJof+TnySpP9YpqX9OsKTu7gMOVtWDwN/ADmA/8ExVbQQ+BN6oqs+B74HnqmpDVV0C3qmqR6pqPbAS2Npl4FZwdgFPVtXDrf+Xhg652OIHgJdbbA9wtMUPA2Mt/jrwc8vtlRZ7CHgRuB+4C9jcJT9J0jXBOiX16Ia+E5CWoAtVdby1PwZ2AuuBiXYh7Xrg93nOfSLJq8Aq4DbgHPBVh7EfZVBUjrexbgJODO3/om1PAU+39hZgG0BVfZPkrwX6/66qfgNIMgWsBY51yE+S1D/rlNQjJ1hSdzXn8zRwrqoeW+ikJCuAd4FNVXUhyV5gRcexA0xU1fZ59s+07RWu/r67LJ+YGWoP9yFJWjqsU1KPXCIodTeWZLZIbQcmgTWzsSQ3Jnmg7Z8Gbmnt2SJ1sa1Hn/dtTAuYBDYnuaeNtSrJvYuccwx4th0/Dtz6H7lJkpYP65TUIydYUnfngeeT/Mhg+cR+BkXorSQ/AFPA4+3Yj4D32jKGGeAD4AzwJXCy68BV9QeDNz590safBNYtcto+YDzJaeApBstCpqvqTwZLOM4OPTwsSVr6rFNSj1I19y6ypPkkWQt83R7+XRKS3Axcqap/2tXLA1W1oe+8JEmjZ52S+ue6VWn5GwM+TXIdcBl4oed8JEkaZp3SsuIdLOkakuQwcOec8GtVdaSPfCRJGmadkhbnBEuSJEmSRsSXXEiSJEnSiDjBkiRJkqQRcYIlSZIkSSPiBEuSJEmSRuRfJyhiyKJcRW0AAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 864x360 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure(figsize=(12, 5))\n",
    "\n",
    "plt.subplot(121)\n",
    "df_trans.scatter(df_trans.petal_length, df_trans.petal_width, c_expr=df_trans.class_)\n",
    "plt.title('Original classes')\n",
    "\n",
    "plt.subplot(122)\n",
    "df_trans.scatter(df_trans.petal_length, df_trans.petal_width, c_expr=df_trans.predicted_kmean_map)\n",
    "plt.title('Predicted classes')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As with any algorithm implemented in `vaex.ml`, K-Means can be used on billions of samples. Fitting takes **under 2 minutes** when applied on the oversampled Iris dataset, numbering over **1 billion** samples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:34:29.843739Z",
     "start_time": "2020-01-14T15:34:29.828941Z"
    },
    "tags": [
     "skip-ci"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of samples in DataFrame: 1,005,000,000\n"
     ]
    }
   ],
   "source": [
    "df = vaex.ml.datasets.load_iris_1e9()\n",
    "n_samples = len(df)\n",
    "print(f'Number of samples in DataFrame: {n_samples:,}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:35:53.139325Z",
     "start_time": "2020-01-14T15:34:29.844971Z"
    },
    "tags": [
     "skip-ci"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Iteration    0, inertia  838974000.003719\n",
      "Iteration    1, inertia  535903134.00030565\n",
      "Iteration    2, inertia  530190921.4848897\n",
      "Iteration    3, inertia  528931941.0337245\n",
      "Iteration    4, inertia  528931941.03372455\n",
      "CPU times: user 4min 7s, sys: 1min 33s, total: 5min 41s\n",
      "Wall time: 1min 23s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "kmeans = vaex.ml.cluster.KMeans(features=features, n_clusters=3, max_iter=100, verbose=True, random_state=31)\n",
    "kmeans.fit(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Supervised learning\n",
    "\n",
    "While `vaex.ml` does not yet implement any supervised machine learning models, it does provide wrappers to several popular libraries such as [scikit-learn](https://scikit-learn.org/), [XGBoost](https://xgboost.readthedocs.io/), [LightGBM](https://lightgbm.readthedocs.io/) and [CatBoost](https://catboost.ai/). \n",
    "\n",
    "The main benefit of these wrappers is that they turn the models into `vaex.ml` transformers. This means the models become part of the DataFrame _state_ and thus can be serialized, and their predictions can be returned as _virtual columns_. This is especially useful for creating various diagnostic plots and evaluating performance metrics at no memory cost, as well as building ensembles. \n",
    "\n",
    "### `Scikit-Learn` example\n",
    "\n",
    "The `vaex.ml.sklearn` module provides convenient wrappers to the `scikit-learn` estimators. In fact, these wrappers can be used with any library that follows the API convention established by `scikit-learn`, i.e. implements the `.fit` and `.transform` methods.\n",
    "\n",
    "Here is an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:35:54.197252Z",
     "start_time": "2020-01-14T15:35:53.143410Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>prediction  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>2           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...         </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>145</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>146</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>147</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>148</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>149</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>1           </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    prediction\n",
       "0    5.9             3.0            4.2             1.5            1         1\n",
       "1    6.1             3.0            4.6             1.4            1         1\n",
       "2    6.6             2.9            4.6             1.3            1         1\n",
       "3    6.7             3.3            5.7             2.1            2         2\n",
       "4    5.5             4.2            1.4             0.2            0         0\n",
       "...  ...             ...            ...             ...            ...       ...\n",
       "145  5.2             3.4            1.4             0.2            0         0\n",
       "146  5.1             3.8            1.6             0.2            0         0\n",
       "147  5.8             2.6            4.0             1.2            1         1\n",
       "148  5.7             3.8            1.7             0.3            0         0\n",
       "149  6.2             2.9            4.3             1.3            1         1"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from vaex.ml.sklearn import Predictor\n",
    "from sklearn.ensemble import GradientBoostingClassifier\n",
    "\n",
    "df = vaex.ml.datasets.load_iris()\n",
    "\n",
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "target = 'class_'\n",
    "\n",
    "model = GradientBoostingClassifier(random_state=42)\n",
    "vaex_model = Predictor(features=features, target=target, model=model, prediction_name='prediction')\n",
    "\n",
    "vaex_model.fit(df=df)\n",
    "\n",
    "df = vaex_model.transform(df)\n",
    "df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One can still train a predictive model on datasets that are too big to fit into memory by leveraging the on-line learners provided by `scikit-learn`. The `vaex.ml.sklearn.IncrementalPredictor` conveniently wraps these learners and provides control on how the data is passed to them from a `vaex` DataFrame. \n",
    "\n",
    "Let us train a model on the oversampled Iris dataset which comprises over 1 billion samples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:48:22.282070Z",
     "start_time": "2020-01-14T15:35:54.198657Z"
    },
    "tags": [
     "skip-ci"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[########################################] 100.00% elapsed time  :   747.59s =  12.5m =  0.2h\n",
      " "
     ]
    },
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                                        </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>prediction  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>            </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>            </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>            </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>            </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>2           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>            </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td>...                                      </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...         </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,995</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,996</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,997</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,998</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,999</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>1           </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#              sepal_length    sepal_width    petal_length    petal_width    class_    prediction\n",
       "0              5.9             3.0            4.2             1.5            1         1\n",
       "1              6.1             3.0            4.6             1.4            1         1\n",
       "2              6.6             2.9            4.6             1.3            1         1\n",
       "3              6.7             3.3            5.7             2.1            2         2\n",
       "4              5.5             4.2            1.4             0.2            0         0\n",
       "...            ...             ...            ...             ...            ...       ...\n",
       "1,004,999,995  5.2             3.4            1.4             0.2            0         0\n",
       "1,004,999,996  5.1             3.8            1.6             0.2            0         0\n",
       "1,004,999,997  5.8             2.6            4.0             1.2            1         1\n",
       "1,004,999,998  5.7             3.8            1.7             0.3            0         0\n",
       "1,004,999,999  6.2             2.9            4.3             1.3            1         1"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from vaex.ml.sklearn import IncrementalPredictor\n",
    "from sklearn.linear_model import SGDClassifier\n",
    "\n",
    "df = vaex.ml.datasets.load_iris_1e9()\n",
    "\n",
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "target = 'class_'\n",
    "\n",
    "model = SGDClassifier(learning_rate='constant', eta0=0.0001, random_state=42)\n",
    "vaex_model = IncrementalPredictor(features=features, target=target, model=model, \n",
    "                                  batch_size=11_000_000, partial_fit_kwargs={'classes':[0, 1, 2]})\n",
    "\n",
    "vaex_model.fit(df=df, progress=True)\n",
    "\n",
    "df = vaex_model.transform(df)\n",
    "df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### `XGBoost` example\n",
    "\n",
    "Libraries such as `XGBoost` provide more options such as validation during training and early stopping for example. We provide wrappers that keeps close to the native API of these libraries, in addition to the `scikit-learn` API. \n",
    "\n",
    "While the following example showcases the `XGBoost` wrapper, `vaex.ml` implements similar wrappers for `LightGBM` and `CatBoost`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:48:23.088854Z",
     "start_time": "2020-01-14T15:48:22.284942Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                                 </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>xgboost_prediction  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>     </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>1.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>     </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>1.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>     </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>1.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>     </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>2.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>     </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>0.0                 </td></tr>\n",
       "<tr><td>...                               </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>80,395</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>0.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>80,396</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>0.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>80,397</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>1.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>80,398</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>0.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>80,399</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>1.0                 </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#       sepal_length    sepal_width    petal_length    petal_width    class_    xgboost_prediction\n",
       "0       5.9             3.0            4.2             1.5            1         1.0\n",
       "1       6.1             3.0            4.6             1.4            1         1.0\n",
       "2       6.6             2.9            4.6             1.3            1         1.0\n",
       "3       6.7             3.3            5.7             2.1            2         2.0\n",
       "4       5.5             4.2            1.4             0.2            0         0.0\n",
       "...     ...             ...            ...             ...            ...       ...\n",
       "80,395  5.2             3.4            1.4             0.2            0         0.0\n",
       "80,396  5.1             3.8            1.6             0.2            0         0.0\n",
       "80,397  5.8             2.6            4.0             1.2            1         1.0\n",
       "80,398  5.7             3.8            1.7             0.3            0         0.0\n",
       "80,399  6.2             2.9            4.3             1.3            1         1.0"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from vaex.ml.xgboost import XGBoostModel\n",
    "\n",
    "df = vaex.ml.datasets.load_iris_1e5()\n",
    "df_train, df_test = df.ml.train_test_split(test_size=0.2, verbose=False)\n",
    "\n",
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "target = 'class_'\n",
    "\n",
    "params = {'learning_rate': 0.1,\n",
    "          'max_depth': 3, \n",
    "          'num_class': 3, \n",
    "          'objective': 'multi:softmax',\n",
    "          'subsample': 1,\n",
    "          'random_state': 42,\n",
    "          'n_jobs': -1}\n",
    "\n",
    "\n",
    "booster = XGBoostModel(features=features, target=target, num_boost_round=500, params=params)\n",
    "booster.fit(df=df_train, evals=[(df_train, 'train'), (df_test, 'test')], early_stopping_rounds=5)\n",
    "\n",
    "df_test = booster.transform(df_train)\n",
    "df_test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## State transfer - pipelines made easy\n",
    "\n",
    "Each `vaex` DataFrame consists of two parts: _data_ and _state_. The _data_ is immutable, and any operation such as filtering, adding new columns, or applying transformers or predictive models just modifies the _state_. This is extremely powerful concept and can completely redefine how we imagine machine learning pipelines. \n",
    "\n",
    "As an example, let us once again create a model based on the Iris dataset. Here, we will create a couple of new features, do a PCA transformation, and finally train a predictive model. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:48:23.212078Z",
     "start_time": "2020-01-14T15:48:23.090568Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>petal_ratio       </th><th>sepal_ratio       </th><th>PCA_0              </th><th>PCA_1              </th><th>PCA_2                 </th><th>PCA_3               </th><th>PCA_4                 </th><th>PCA_5                 </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.4           </td><td>3.0          </td><td>4.5           </td><td>1.5          </td><td>1       </td><td>3.0               </td><td>1.8               </td><td>-1.510547480171215 </td><td>0.3611524321126822 </td><td>-0.4005106138591812   </td><td>0.5491844107628985  </td><td>0.21135370342329635   </td><td>-0.009542243224854377 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>4.8           </td><td>3.4          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>8.0               </td><td>1.411764705882353 </td><td>4.447550641536847  </td><td>0.2799644730487585 </td><td>-0.04904458661276928  </td><td>0.18719360579644695 </td><td>0.10928493945448532   </td><td>0.005228919010020094  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.9           </td><td>3.1          </td><td>4.9           </td><td>1.5          </td><td>1       </td><td>3.266666666666667 </td><td>2.2258064516129035</td><td>-1.777649528149752 </td><td>-0.6082889770845891</td><td>0.48007833550651513   </td><td>-0.37762011866831335</td><td>0.05174472701894024   </td><td>-0.04673816474220924  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>4.4           </td><td>3.2          </td><td>1.3           </td><td>0.2          </td><td>0       </td><td>6.5               </td><td>1.375             </td><td>3.400548263702555  </td><td>1.437036928591846  </td><td>-0.3662652846960042   </td><td>0.23420836198441913 </td><td>0.05750021481634099   </td><td>-0.023055011653267066 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.6           </td><td>2.8          </td><td>4.9           </td><td>2.0          </td><td>2       </td><td>2.45              </td><td>2.0               </td><td>-2.3245098766222094</td><td>0.14710673877401348</td><td>-0.5150809942258257   </td><td>0.5471824391426298  </td><td>-0.12154714382375817  </td><td>0.0044686197532133876 </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...               </td><td>...               </td><td>...                </td><td>...                </td><td>...                   </td><td>...                 </td><td>...                   </td><td>...                   </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>115</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>6.999999999999999 </td><td>1.5294117647058825</td><td>3.623794583238953  </td><td>0.8255759252729563 </td><td>0.23453320686724874   </td><td>-0.17599408825208826</td><td>-0.04687036865354327  </td><td>-0.02424621891240747  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>116</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>8.0               </td><td>1.3421052631578947</td><td>4.42115266246093   </td><td>0.22287505533663704</td><td>0.4450642830179705    </td><td>0.2184424557783562  </td><td>0.14504752606375293   </td><td>0.07229123907677276   </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>117</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>3.3333333333333335</td><td>2.230769230769231 </td><td>-1.069062832993727 </td><td>0.3874258314654399 </td><td>-0.4471767749236783   </td><td>-0.2956609879568117 </td><td>-0.0010695982441835394</td><td>-0.0065225306610744715</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>118</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>5.666666666666667 </td><td>1.5000000000000002</td><td>2.2846521048417037 </td><td>1.1920826609681359 </td><td>0.8273738848637026    </td><td>-0.21048946462725737</td><td>0.03381892388998425   </td><td>0.018792165273013528  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>119</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>3.3076923076923075</td><td>2.137931034482759 </td><td>-1.2988229958748452</td><td>0.06960434514054464</td><td>-0.0012167985718341268</td><td>-0.24072255219180883</td><td>0.05282732890885841   </td><td>-0.032459999314411514 </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    petal_ratio         sepal_ratio         PCA_0                PCA_1                PCA_2                   PCA_3                 PCA_4                   PCA_5\n",
       "0    5.4             3.0            4.5             1.5            1         3.0                 1.8                 -1.510547480171215   0.3611524321126822   -0.4005106138591812     0.5491844107628985    0.21135370342329635     -0.009542243224854377\n",
       "1    4.8             3.4            1.6             0.2            0         8.0                 1.411764705882353   4.447550641536847    0.2799644730487585   -0.04904458661276928    0.18719360579644695   0.10928493945448532     0.005228919010020094\n",
       "2    6.9             3.1            4.9             1.5            1         3.266666666666667   2.2258064516129035  -1.777649528149752   -0.6082889770845891  0.48007833550651513     -0.37762011866831335  0.05174472701894024     -0.04673816474220924\n",
       "3    4.4             3.2            1.3             0.2            0         6.5                 1.375               3.400548263702555    1.437036928591846    -0.3662652846960042     0.23420836198441913   0.05750021481634099     -0.023055011653267066\n",
       "4    5.6             2.8            4.9             2.0            2         2.45                2.0                 -2.3245098766222094  0.14710673877401348  -0.5150809942258257     0.5471824391426298    -0.12154714382375817    0.0044686197532133876\n",
       "...  ...             ...            ...             ...            ...       ...                 ...                 ...                  ...                  ...                     ...                   ...                     ...\n",
       "115  5.2             3.4            1.4             0.2            0         6.999999999999999   1.5294117647058825  3.623794583238953    0.8255759252729563   0.23453320686724874     -0.17599408825208826  -0.04687036865354327    -0.02424621891240747\n",
       "116  5.1             3.8            1.6             0.2            0         8.0                 1.3421052631578947  4.42115266246093     0.22287505533663704  0.4450642830179705      0.2184424557783562    0.14504752606375293     0.07229123907677276\n",
       "117  5.8             2.6            4.0             1.2            1         3.3333333333333335  2.230769230769231   -1.069062832993727   0.3874258314654399   -0.4471767749236783     -0.2956609879568117   -0.0010695982441835394  -0.0065225306610744715\n",
       "118  5.7             3.8            1.7             0.3            0         5.666666666666667   1.5000000000000002  2.2846521048417037   1.1920826609681359   0.8273738848637026      -0.21048946462725737  0.03381892388998425     0.018792165273013528\n",
       "119  6.2             2.9            4.3             1.3            1         3.3076923076923075  2.137931034482759   -1.2988229958748452  0.06960434514054464  -0.0012167985718341268  -0.24072255219180883  0.05282732890885841     -0.032459999314411514"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Load data and split it in train and test sets\n",
    "df = vaex.ml.datasets.load_iris()\n",
    "df_train, df_test = df.ml.train_test_split(test_size=0.2, verbose=False)\n",
    "\n",
    "# Create new features\n",
    "df_train['petal_ratio'] = df_train.petal_length / df_train.petal_width\n",
    "df_train['sepal_ratio'] = df_train.sepal_length / df_train.sepal_width\n",
    "\n",
    "# Do a PCA transformation\n",
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width', 'petal_ratio', 'sepal_ratio']\n",
    "pca = vaex.ml.PCA(features=features, n_components=6)\n",
    "df_train = pca.fit_transform(df_train)\n",
    "\n",
    "# Display the training DataFrame at this stage\n",
    "df_train"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "At this point, we are ready to train a predictive model. In this example, let's use `LightGBM` with its `scikit-learn` API. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:48:59.613716Z",
     "start_time": "2020-01-14T15:48:59.512019Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>petal_ratio       </th><th>sepal_ratio       </th><th>PCA_0              </th><th>PCA_1              </th><th>PCA_2                 </th><th>PCA_3               </th><th>PCA_4                 </th><th>PCA_5                 </th><th>prediction  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.4           </td><td>3.0          </td><td>4.5           </td><td>1.5          </td><td>1       </td><td>3.0               </td><td>1.8               </td><td>-1.510547480171215 </td><td>0.3611524321126822 </td><td>-0.4005106138591812   </td><td>0.5491844107628985  </td><td>0.21135370342329635   </td><td>-0.009542243224854377 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>4.8           </td><td>3.4          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>8.0               </td><td>1.411764705882353 </td><td>4.447550641536847  </td><td>0.2799644730487585 </td><td>-0.04904458661276928  </td><td>0.18719360579644695 </td><td>0.10928493945448532   </td><td>0.005228919010020094  </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.9           </td><td>3.1          </td><td>4.9           </td><td>1.5          </td><td>1       </td><td>3.266666666666667 </td><td>2.2258064516129035</td><td>-1.777649528149752 </td><td>-0.6082889770845891</td><td>0.48007833550651513   </td><td>-0.37762011866831335</td><td>0.05174472701894024   </td><td>-0.04673816474220924  </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>4.4           </td><td>3.2          </td><td>1.3           </td><td>0.2          </td><td>0       </td><td>6.5               </td><td>1.375             </td><td>3.400548263702555  </td><td>1.437036928591846  </td><td>-0.3662652846960042   </td><td>0.23420836198441913 </td><td>0.05750021481634099   </td><td>-0.023055011653267066 </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.6           </td><td>2.8          </td><td>4.9           </td><td>2.0          </td><td>2       </td><td>2.45              </td><td>2.0               </td><td>-2.3245098766222094</td><td>0.14710673877401348</td><td>-0.5150809942258257   </td><td>0.5471824391426298  </td><td>-0.12154714382375817  </td><td>0.0044686197532133876 </td><td>2           </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...               </td><td>...               </td><td>...                </td><td>...                </td><td>...                   </td><td>...                 </td><td>...                   </td><td>...                   </td><td>...         </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>115</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>6.999999999999999 </td><td>1.5294117647058825</td><td>3.623794583238953  </td><td>0.8255759252729563 </td><td>0.23453320686724874   </td><td>-0.17599408825208826</td><td>-0.04687036865354327  </td><td>-0.02424621891240747  </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>116</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>8.0               </td><td>1.3421052631578947</td><td>4.42115266246093   </td><td>0.22287505533663704</td><td>0.4450642830179705    </td><td>0.2184424557783562  </td><td>0.14504752606375293   </td><td>0.07229123907677276   </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>117</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>3.3333333333333335</td><td>2.230769230769231 </td><td>-1.069062832993727 </td><td>0.3874258314654399 </td><td>-0.4471767749236783   </td><td>-0.2956609879568117 </td><td>-0.0010695982441835394</td><td>-0.0065225306610744715</td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>118</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>5.666666666666667 </td><td>1.5000000000000002</td><td>2.2846521048417037 </td><td>1.1920826609681359 </td><td>0.8273738848637026    </td><td>-0.21048946462725737</td><td>0.03381892388998425   </td><td>0.018792165273013528  </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>119</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>3.3076923076923075</td><td>2.137931034482759 </td><td>-1.2988229958748452</td><td>0.06960434514054464</td><td>-0.0012167985718341268</td><td>-0.24072255219180883</td><td>0.05282732890885841   </td><td>-0.032459999314411514 </td><td>1           </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    petal_ratio         sepal_ratio         PCA_0                PCA_1                PCA_2                   PCA_3                 PCA_4                   PCA_5                   prediction\n",
       "0    5.4             3.0            4.5             1.5            1         3.0                 1.8                 -1.510547480171215   0.3611524321126822   -0.4005106138591812     0.5491844107628985    0.21135370342329635     -0.009542243224854377   1\n",
       "1    4.8             3.4            1.6             0.2            0         8.0                 1.411764705882353   4.447550641536847    0.2799644730487585   -0.04904458661276928    0.18719360579644695   0.10928493945448532     0.005228919010020094    0\n",
       "2    6.9             3.1            4.9             1.5            1         3.266666666666667   2.2258064516129035  -1.777649528149752   -0.6082889770845891  0.48007833550651513     -0.37762011866831335  0.05174472701894024     -0.04673816474220924    1\n",
       "3    4.4             3.2            1.3             0.2            0         6.5                 1.375               3.400548263702555    1.437036928591846    -0.3662652846960042     0.23420836198441913   0.05750021481634099     -0.023055011653267066   0\n",
       "4    5.6             2.8            4.9             2.0            2         2.45                2.0                 -2.3245098766222094  0.14710673877401348  -0.5150809942258257     0.5471824391426298    -0.12154714382375817    0.0044686197532133876   2\n",
       "...  ...             ...            ...             ...            ...       ...                 ...                 ...                  ...                  ...                     ...                   ...                     ...                     ...\n",
       "115  5.2             3.4            1.4             0.2            0         6.999999999999999   1.5294117647058825  3.623794583238953    0.8255759252729563   0.23453320686724874     -0.17599408825208826  -0.04687036865354327    -0.02424621891240747    0\n",
       "116  5.1             3.8            1.6             0.2            0         8.0                 1.3421052631578947  4.42115266246093     0.22287505533663704  0.4450642830179705      0.2184424557783562    0.14504752606375293     0.07229123907677276     0\n",
       "117  5.8             2.6            4.0             1.2            1         3.3333333333333335  2.230769230769231   -1.069062832993727   0.3874258314654399   -0.4471767749236783     -0.2956609879568117   -0.0010695982441835394  -0.0065225306610744715  1\n",
       "118  5.7             3.8            1.7             0.3            0         5.666666666666667   1.5000000000000002  2.2846521048417037   1.1920826609681359   0.8273738848637026      -0.21048946462725737  0.03381892388998425     0.018792165273013528    0\n",
       "119  6.2             2.9            4.3             1.3            1         3.3076923076923075  2.137931034482759   -1.2988229958748452  0.06960434514054464  -0.0012167985718341268  -0.24072255219180883  0.05282732890885841     -0.032459999314411514   1"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import lightgbm\n",
    "\n",
    "features = df_train.get_column_names(regex='^PCA')\n",
    "\n",
    "booster = lightgbm.LGBMClassifier()\n",
    "\n",
    "vaex_model = Predictor(model=booster, features=features, target='class_')\n",
    "\n",
    "vaex_model.fit(df=df_train)\n",
    "df_train = vaex_model.transform(df_train)\n",
    "\n",
    "df_train"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The final `df_train` DataFrame contains all the features we created, including the predictions right at the end. Now, we would like to apply the same transformations to the test set. All we need to do, is to simply extract the _state_ from `df_train` and apply it to `df_test`. This will propagate all the changes that were made to the training set on the test set.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:48:23.494415Z",
     "start_time": "2020-01-14T15:48:23.394030Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                             </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>petal_ratio       </th><th>sepal_ratio       </th><th>PCA_0              </th><th>PCA_1               </th><th>PCA_2               </th><th>PCA_3               </th><th>PCA_4               </th><th>PCA_5                </th><th>prediction  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i> </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>2.8000000000000003</td><td>1.9666666666666668</td><td>-1.642627940409072 </td><td>0.49931302910747727 </td><td>-0.06308800806664466</td><td>0.10842057110641677 </td><td>-0.03924298664189224</td><td>-0.027394439700272822</td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i> </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>3.2857142857142856</td><td>2.033333333333333 </td><td>-1.445047446393471 </td><td>-0.1019091578746504 </td><td>-0.01899012239493801</td><td>0.020980767646090408</td><td>0.1614215276667148  </td><td>-0.02716639637934938 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i> </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>3.538461538461538 </td><td>2.2758620689655173</td><td>-1.330564613235537 </td><td>-0.41978474749131267</td><td>0.1759590589290671  </td><td>-0.4631301992308477 </td><td>0.08304243689815374 </td><td>-0.033351733677429274</td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i> </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>2.7142857142857144</td><td>2.0303030303030303</td><td>-2.6719170661531013</td><td>-0.9149428897499291 </td><td>0.4156162725009377  </td><td>0.34633692661436644 </td><td>0.03742964707590906 </td><td>-0.013254286196245774</td><td>2           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i> </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>6.999999999999999 </td><td>1.3095238095238095</td><td>3.6322930267831404 </td><td>0.8198526437905096  </td><td>1.046277579362938   </td><td>0.09738737839850209 </td><td>0.09412658096734221 </td><td>0.1329137026697501   </td><td>0           </td></tr>\n",
       "<tr><td>...                           </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...               </td><td>...               </td><td>...                </td><td>...                 </td><td>...                 </td><td>...                 </td><td>...                 </td><td>...                  </td><td>...         </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>25</i></td><td>5.5           </td><td>2.5          </td><td>4.0           </td><td>1.3          </td><td>1       </td><td>3.0769230769230766</td><td>2.2               </td><td>-1.2523120088600896</td><td>0.5975071562677784  </td><td>-0.7019801415469216 </td><td>-0.11489031841855571</td><td>-0.03615945782087869</td><td>0.005496321827264977 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>26</i></td><td>5.8           </td><td>2.7          </td><td>3.9           </td><td>1.2          </td><td>1       </td><td>3.25              </td><td>2.148148148148148 </td><td>-1.0792352165904657</td><td>0.5236883751378523  </td><td>-0.34037717939532286</td><td>-0.23743695029955128</td><td>-0.00936891422024664</td><td>-0.02184110533380834 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>27</i></td><td>4.4           </td><td>2.9          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>6.999999999999999 </td><td>1.517241379310345 </td><td>3.7422969192506095 </td><td>1.048460304741977   </td><td>-0.636475521315278  </td><td>0.07623157913054074 </td><td>0.004215355833312173</td><td>-0.06354157393133958 </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>28</i></td><td>4.5           </td><td>2.3          </td><td>1.3           </td><td>0.3          </td><td>0       </td><td>4.333333333333334 </td><td>1.956521739130435 </td><td>1.4537380535696471 </td><td>2.4197864889383505  </td><td>-1.0301500321688102 </td><td>-0.5150263062576134 </td><td>-0.2631218962099228 </td><td>-0.06608059456656257 </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>29</i></td><td>6.9           </td><td>3.2          </td><td>5.7           </td><td>2.3          </td><td>2       </td><td>2.4782608695652177</td><td>2.15625           </td><td>-2.963110301521378 </td><td>-0.924626055589704  </td><td>0.44833006106219797 </td><td>0.20994670504662372 </td><td>-0.2012725506779131 </td><td>-0.018900414287719353</td><td>2           </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    petal_ratio         sepal_ratio         PCA_0                PCA_1                 PCA_2                 PCA_3                 PCA_4                 PCA_5                  prediction\n",
       "0    5.9             3.0            4.2             1.5            1         2.8000000000000003  1.9666666666666668  -1.642627940409072   0.49931302910747727   -0.06308800806664466  0.10842057110641677   -0.03924298664189224  -0.027394439700272822  1\n",
       "1    6.1             3.0            4.6             1.4            1         3.2857142857142856  2.033333333333333   -1.445047446393471   -0.1019091578746504   -0.01899012239493801  0.020980767646090408  0.1614215276667148    -0.02716639637934938   1\n",
       "2    6.6             2.9            4.6             1.3            1         3.538461538461538   2.2758620689655173  -1.330564613235537   -0.41978474749131267  0.1759590589290671    -0.4631301992308477   0.08304243689815374   -0.033351733677429274  1\n",
       "3    6.7             3.3            5.7             2.1            2         2.7142857142857144  2.0303030303030303  -2.6719170661531013  -0.9149428897499291   0.4156162725009377    0.34633692661436644   0.03742964707590906   -0.013254286196245774  2\n",
       "4    5.5             4.2            1.4             0.2            0         6.999999999999999   1.3095238095238095  3.6322930267831404   0.8198526437905096    1.046277579362938     0.09738737839850209   0.09412658096734221   0.1329137026697501     0\n",
       "...  ...             ...            ...             ...            ...       ...                 ...                 ...                  ...                   ...                   ...                   ...                   ...                    ...\n",
       "25   5.5             2.5            4.0             1.3            1         3.0769230769230766  2.2                 -1.2523120088600896  0.5975071562677784    -0.7019801415469216   -0.11489031841855571  -0.03615945782087869  0.005496321827264977   1\n",
       "26   5.8             2.7            3.9             1.2            1         3.25                2.148148148148148   -1.0792352165904657  0.5236883751378523    -0.34037717939532286  -0.23743695029955128  -0.00936891422024664  -0.02184110533380834   1\n",
       "27   4.4             2.9            1.4             0.2            0         6.999999999999999   1.517241379310345   3.7422969192506095   1.048460304741977     -0.636475521315278    0.07623157913054074   0.004215355833312173  -0.06354157393133958   0\n",
       "28   4.5             2.3            1.3             0.3            0         4.333333333333334   1.956521739130435   1.4537380535696471   2.4197864889383505    -1.0301500321688102   -0.5150263062576134   -0.2631218962099228   -0.06608059456656257   0\n",
       "29   6.9             3.2            5.7             2.3            2         2.4782608695652177  2.15625             -2.963110301521378   -0.924626055589704    0.44833006106219797   0.20994670504662372   -0.2012725506779131   -0.018900414287719353  2"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state = df_train.state_get()\n",
    "\n",
    "df_test.state_set(state)\n",
    "df_test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And just like that `df_test` contains all the columns, transformations and the prediction we modelled on the training set. The state can be easily serialized to disk in a form of a JSON file. This makes deployment of a machine learning model as trivial as simply copying a JSON file from one environment to another."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-14T15:48:23.601997Z",
     "start_time": "2020-01-14T15:48:23.496077Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                             </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>petal_ratio       </th><th>sepal_ratio       </th><th>PCA_0              </th><th>PCA_1               </th><th>PCA_2               </th><th>PCA_3               </th><th>PCA_4               </th><th>PCA_5                </th><th>prediction  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i> </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>2.8000000000000003</td><td>1.9666666666666668</td><td>-1.642627940409072 </td><td>0.49931302910747727 </td><td>-0.06308800806664466</td><td>0.10842057110641677 </td><td>-0.03924298664189224</td><td>-0.027394439700272822</td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i> </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>3.2857142857142856</td><td>2.033333333333333 </td><td>-1.445047446393471 </td><td>-0.1019091578746504 </td><td>-0.01899012239493801</td><td>0.020980767646090408</td><td>0.1614215276667148  </td><td>-0.02716639637934938 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i> </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>3.538461538461538 </td><td>2.2758620689655173</td><td>-1.330564613235537 </td><td>-0.41978474749131267</td><td>0.1759590589290671  </td><td>-0.4631301992308477 </td><td>0.08304243689815374 </td><td>-0.033351733677429274</td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i> </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>2.7142857142857144</td><td>2.0303030303030303</td><td>-2.6719170661531013</td><td>-0.9149428897499291 </td><td>0.4156162725009377  </td><td>0.34633692661436644 </td><td>0.03742964707590906 </td><td>-0.013254286196245774</td><td>2           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i> </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>6.999999999999999 </td><td>1.3095238095238095</td><td>3.6322930267831404 </td><td>0.8198526437905096  </td><td>1.046277579362938   </td><td>0.09738737839850209 </td><td>0.09412658096734221 </td><td>0.1329137026697501   </td><td>0           </td></tr>\n",
       "<tr><td>...                           </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...               </td><td>...               </td><td>...                </td><td>...                 </td><td>...                 </td><td>...                 </td><td>...                 </td><td>...                  </td><td>...         </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>25</i></td><td>5.5           </td><td>2.5          </td><td>4.0           </td><td>1.3          </td><td>1       </td><td>3.0769230769230766</td><td>2.2               </td><td>-1.2523120088600896</td><td>0.5975071562677784  </td><td>-0.7019801415469216 </td><td>-0.11489031841855571</td><td>-0.03615945782087869</td><td>0.005496321827264977 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>26</i></td><td>5.8           </td><td>2.7          </td><td>3.9           </td><td>1.2          </td><td>1       </td><td>3.25              </td><td>2.148148148148148 </td><td>-1.0792352165904657</td><td>0.5236883751378523  </td><td>-0.34037717939532286</td><td>-0.23743695029955128</td><td>-0.00936891422024664</td><td>-0.02184110533380834 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>27</i></td><td>4.4           </td><td>2.9          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>6.999999999999999 </td><td>1.517241379310345 </td><td>3.7422969192506095 </td><td>1.048460304741977   </td><td>-0.636475521315278  </td><td>0.07623157913054074 </td><td>0.004215355833312173</td><td>-0.06354157393133958 </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>28</i></td><td>4.5           </td><td>2.3          </td><td>1.3           </td><td>0.3          </td><td>0       </td><td>4.333333333333334 </td><td>1.956521739130435 </td><td>1.4537380535696471 </td><td>2.4197864889383505  </td><td>-1.0301500321688102 </td><td>-0.5150263062576134 </td><td>-0.2631218962099228 </td><td>-0.06608059456656257 </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>29</i></td><td>6.9           </td><td>3.2          </td><td>5.7           </td><td>2.3          </td><td>2       </td><td>2.4782608695652177</td><td>2.15625           </td><td>-2.963110301521378 </td><td>-0.924626055589704  </td><td>0.44833006106219797 </td><td>0.20994670504662372 </td><td>-0.2012725506779131 </td><td>-0.018900414287719353</td><td>2           </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    petal_ratio         sepal_ratio         PCA_0                PCA_1                 PCA_2                 PCA_3                 PCA_4                 PCA_5                  prediction\n",
       "0    5.9             3.0            4.2             1.5            1         2.8000000000000003  1.9666666666666668  -1.642627940409072   0.49931302910747727   -0.06308800806664466  0.10842057110641677   -0.03924298664189224  -0.027394439700272822  1\n",
       "1    6.1             3.0            4.6             1.4            1         3.2857142857142856  2.033333333333333   -1.445047446393471   -0.1019091578746504   -0.01899012239493801  0.020980767646090408  0.1614215276667148    -0.02716639637934938   1\n",
       "2    6.6             2.9            4.6             1.3            1         3.538461538461538   2.2758620689655173  -1.330564613235537   -0.41978474749131267  0.1759590589290671    -0.4631301992308477   0.08304243689815374   -0.033351733677429274  1\n",
       "3    6.7             3.3            5.7             2.1            2         2.7142857142857144  2.0303030303030303  -2.6719170661531013  -0.9149428897499291   0.4156162725009377    0.34633692661436644   0.03742964707590906   -0.013254286196245774  2\n",
       "4    5.5             4.2            1.4             0.2            0         6.999999999999999   1.3095238095238095  3.6322930267831404   0.8198526437905096    1.046277579362938     0.09738737839850209   0.09412658096734221   0.1329137026697501     0\n",
       "...  ...             ...            ...             ...            ...       ...                 ...                 ...                  ...                   ...                   ...                   ...                   ...                    ...\n",
       "25   5.5             2.5            4.0             1.3            1         3.0769230769230766  2.2                 -1.2523120088600896  0.5975071562677784    -0.7019801415469216   -0.11489031841855571  -0.03615945782087869  0.005496321827264977   1\n",
       "26   5.8             2.7            3.9             1.2            1         3.25                2.148148148148148   -1.0792352165904657  0.5236883751378523    -0.34037717939532286  -0.23743695029955128  -0.00936891422024664  -0.02184110533380834   1\n",
       "27   4.4             2.9            1.4             0.2            0         6.999999999999999   1.517241379310345   3.7422969192506095   1.048460304741977     -0.636475521315278    0.07623157913054074   0.004215355833312173  -0.06354157393133958   0\n",
       "28   4.5             2.3            1.3             0.3            0         4.333333333333334   1.956521739130435   1.4537380535696471   2.4197864889383505    -1.0301500321688102   -0.5150263062576134   -0.2631218962099228   -0.06608059456656257   0\n",
       "29   6.9             3.2            5.7             2.3            2         2.4782608695652177  2.15625             -2.963110301521378   -0.924626055589704    0.44833006106219797   0.20994670504662372   -0.2012725506779131   -0.018900414287719353  2"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_train.state_write('./iris_model.json')\n",
    "\n",
    "df_test.state_load('./iris_model.json')\n",
    "df_test"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
